package com.yupi.springbootinit.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.yupi.springbootinit.annotation.OperationLog;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.service.OperationLogService;
import com.yupi.springbootinit.utils.OperationLogUtils;
import com.yupi.springbootinit.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 操作日志切面
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Resource
    private OperationLogService operationLogService;

    private final ObjectMapper objectMapper;

    public OperationLogAspect() {
        this.objectMapper = new ObjectMapper();
        // 配置ObjectMapper以更好地处理序列化
        this.objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        this.objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    /**
     * 定义切点：所有带有@OperationLog注解的方法
     */
    @Pointcut("@annotation(com.yupi.springbootinit.annotation.OperationLog)")
    public void operationLogPointcut() {
    }

    /**
     * 环绕通知：记录操作日志
     */
    @Around("operationLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        Exception exception = null;
        
        // 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLogAnnotation = method.getAnnotation(OperationLog.class);
        
        // 在方法执行前查询原始数据（仅对需要记录数据变更的操作）
        Object beforeData = null;
        Object primaryKeyValue = null;
        if (operationLogAnnotation != null && operationLogAnnotation.recordDataChange()) {
            try {
                Object[] beforeDataResult = queryBeforeDataAndPrimaryKey(joinPoint, operationLogAnnotation);
                if (beforeDataResult != null && beforeDataResult.length == 2) {
                    beforeData = beforeDataResult[0];
                    primaryKeyValue = beforeDataResult[1];
                }
            } catch (Exception e) {
                log.warn("查询修改前数据失败: {}", e.getMessage());
            }
        }

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            return result;
        } catch (Exception e) {
            exception = e;
            throw e;
        } finally {
            // 记录操作日志
            recordOperationLog(joinPoint, result, exception, System.currentTimeMillis() - startTime, beforeData, primaryKeyValue);
        }
    }

    /**
     * 记录操作日志
     */
    private void recordOperationLog(ProceedingJoinPoint joinPoint, Object result, Exception exception, long executionTime, Object beforeData, Object primaryKeyValue) {
        try {
            // 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            // 获取注解信息
            OperationLog operationLogAnnotation = method.getAnnotation(OperationLog.class);
            if (operationLogAnnotation == null) {
                return;
            }

            // 获取当前用户信息
            User currentUser = OperationLogUtils.getCurrentUser();
            Long userId = currentUser != null ? currentUser.getId() : 0L; // 未登录用户使用0
            String userName = currentUser != null ? currentUser.getUserName() : "未登录用户";

            // 获取IP地址
            String ipAddress = OperationLogUtils.getClientIpAddress();

            // 构建JSON格式的操作内容
            Map<String, Object> contentMap = new HashMap<>();

            // 添加操作描述
            if (StringUtils.hasText(operationLogAnnotation.description())) {
                contentMap.put("description", operationLogAnnotation.description());
            }

            // 记录请求参数（带参数名）
            if (operationLogAnnotation.recordParams()) {
                String[] paramNames = signature.getParameterNames();
                Object[] args = joinPoint.getArgs();
                Map<String, Object> paramsMap = new HashMap<>();
                if (paramNames != null && args != null && paramNames.length == args.length) {
                    for (int i = 0; i < paramNames.length; i++) {
                        Object arg = args[i];
                        // 过滤不可序列化参数
                        if (isSerializable(arg)) {
                            paramsMap.put(paramNames[i], arg);
                        } else {
                            paramsMap.put(paramNames[i], "[" + (arg == null ? "null" : arg.getClass().getSimpleName()) + " 不可序列化]");
                        }
                    }
                }
                contentMap.put("requestParams", paramsMap);
            }

            // 记录返回结果
            if (operationLogAnnotation.recordResult() && result != null) {
                try {
                    contentMap.put("responseResult", result);
                } catch (Exception e) {
                    contentMap.put("responseResult", "[序列化失败]");
                }
            }

            // 记录数据变更对比（仅适用于更新操作）
            if (operationLogAnnotation.recordDataChange() && exception == null && beforeData != null) {
                try {
                    Map<String, Object> dataChange = buildDataChangeRecord(joinPoint, operationLogAnnotation, beforeData, primaryKeyValue);
                    if (dataChange != null && !dataChange.isEmpty()) {
                        contentMap.put("dataChange", dataChange);
                    }
                } catch (Exception e) {
                    log.warn("记录数据变更失败: {}", e.getMessage());
                    contentMap.put("dataChange", "[数据变更记录失败: " + e.getMessage() + "]");
                }
            }

            // 记录执行时间
            contentMap.put("executionTime", executionTime);

            // 记录执行状态和异常信息
            if (exception != null) {
                contentMap.put("status", "FAILURE");
                contentMap.put("errorMessage", exception.getMessage());
            } else {
                contentMap.put("status", "SUCCESS");
            }

            // 转换为JSON字符串
            String contentJson;
            try {
                contentJson = objectMapper.writeValueAsString(contentMap);
            } catch (Exception e) {
                log.error("操作日志内容JSON序列化失败", e);
                contentJson = "{\"status\":\"失败\",\"errorMessage\":\"日志内容序列化失败\"}";
            }

            // 异步记录日志
            operationLogService.recordLogAsync(
                    userId,
                    userName,
                    operationLogAnnotation.module(),
                    operationLogAnnotation.operation(),
                    contentJson,
                    ipAddress
            );
            log.info("操作日志内容 contentMap: {}", contentMap);

        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 过滤掉不可序列化的参数
     */
    private Object[] filterSerializableArgs(Object[] args) {
        if (args == null || args.length == 0) {
            return args;
        }

        List<Object> filteredArgs = new ArrayList<>();
        for (Object arg : args) {
            if (arg == null) {
                filteredArgs.add(null);
            } else if (isSerializable(arg)) {
                filteredArgs.add(arg);
            } else {
                // 对于不可序列化的对象，记录其类型信息
                filteredArgs.add("[" + arg.getClass().getSimpleName() + " 对象]");
            }
        }
        return filteredArgs.toArray();
    }

    /**
     * 判断对象是否可以序列化
     */
    private boolean isSerializable(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof HttpServletRequest ||
                obj instanceof HttpServletResponse ||
                obj.getClass().getName().contains("springframework.web") ||
                obj.getClass().getName().contains("servlet")) {
            return false;
        }
        if (obj instanceof String ||
                obj instanceof Number ||
                obj instanceof Boolean ||
                obj.getClass().isPrimitive()) {
            return true;
        }
        try {
            objectMapper.writeValueAsString(obj);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 在方法执行前查询原始数据和主键值
     * @return Object[] {beforeData, primaryKeyValue}
     */
    private Object[] queryBeforeDataAndPrimaryKey(ProceedingJoinPoint joinPoint, OperationLog operationLogAnnotation) {
        try {
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return null;
            }

            // 查找包含主键的参数对象
            Object updateRequest = null;
            Object primaryKeyValue = null;

            for (Object arg : args) {
                if (arg != null && !isSystemObject(arg)) {
                    try {
                        // 尝试获取主键值
                        Field primaryKeyField = findField(arg.getClass(), operationLogAnnotation.primaryKeyField());
                        if (primaryKeyField != null) {
                            primaryKeyField.setAccessible(true);
                            primaryKeyValue = primaryKeyField.get(arg);
                            updateRequest = arg;
                            break;
                        }
                    } catch (Exception e) {
                        // 继续查找下一个参数
                    }
                }
            }

            if (primaryKeyValue == null) {
                log.warn("无法从请求参数中提取主键值");
                return null;
            }

            // 查询修改前的数据
            Object beforeData = queryDataById(joinPoint, primaryKeyValue);
            
            log.info("成功查询修改前数据，主键值: {}, 数据: {}", primaryKeyValue, beforeData != null ? "存在" : "不存在");
            
            return new Object[]{beforeData, primaryKeyValue};

        } catch (Exception e) {
            log.error("查询修改前数据和主键失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据主键查询数据
     */
    private Object queryDataById(ProceedingJoinPoint joinPoint, Object primaryKeyValue) {
        try {
            // 获取目标类名，推断对应的Service
            String className = joinPoint.getTarget().getClass().getSimpleName();
            Object service = null;

            // 通过类型获取Service Bean
            if (className.contains("UserController")) {
                try {
                    service = SpringContextUtils.getBean(com.yupi.springbootinit.service.UserService.class);
                } catch (Exception e) {
                    log.warn("通过UserService接口获取Bean失败: {}", e.getMessage());
                }
            } else if (className.contains("DepartmentController")) {
                try {
                    service = SpringContextUtils.getBean(com.yupi.springbootinit.service.DepartmentService.class);
                } catch (Exception e) {
                    log.warn("通过DepartmentService接口获取Bean失败: {}", e.getMessage());
                }
            }

            if (service == null) {
                log.warn("无法获取对应的Service Bean，Controller类名: {}", className);
                return null;
            }

            // 调用getById方法查询原始数据
            try {
                java.lang.reflect.Method getByIdMethod = service.getClass().getMethod("getById", java.io.Serializable.class);
                Object beforeData = getByIdMethod.invoke(service, primaryKeyValue);
                log.info("查询原始数据成功，Controller: {}, 主键值: {}, 数据: {}", 
                    className, primaryKeyValue, beforeData != null ? "存在" : "不存在");
                return beforeData;
            } catch (Exception e) {
                log.warn("调用getById方法失败，Service类: {}, 主键值: {}, 错误: {}", 
                    service.getClass().getSimpleName(), primaryKeyValue, e.getMessage());
                return null;
            }

        } catch (Exception e) {
            log.error("查询原始数据失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 构建数据变更记录
     */
    private Map<String, Object> buildDataChangeRecord(ProceedingJoinPoint joinPoint, OperationLog operationLogAnnotation, Object beforeData, Object primaryKeyValue) {
        try {
            // 获取修改后的数据（从请求参数中提取）
            Object afterData = null;
            Object[] args = joinPoint.getArgs();
            
            if (args != null && args.length > 0) {
                for (Object arg : args) {
                    if (arg != null && !isSystemObject(arg)) {
                        try {
                            // 检查是否包含主键字段
                            Field primaryKeyField = findField(arg.getClass(), operationLogAnnotation.primaryKeyField());
                            if (primaryKeyField != null) {
                                afterData = arg;
                                break;
                            }
                        } catch (Exception e) {
                            // 继续查找下一个参数
                        }
                    }
                }
            }

            // 构建数据变更记录
            Map<String, Object> dataChange = new HashMap<>();
            dataChange.put("primaryKey", operationLogAnnotation.primaryKeyField());
            dataChange.put("primaryKeyValue", primaryKeyValue);
            dataChange.put("beforeData", beforeData);
            dataChange.put("afterData", afterData);
            dataChange.put("changeType", "UPDATE");

            log.info("构建数据变更记录成功，主键值: {}", primaryKeyValue);
            return dataChange;

        } catch (Exception e) {
            log.warn("构建数据变更记录失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 判断是否为系统对象（如HttpServletRequest等）
     */
    private boolean isSystemObject(Object obj) {
        if (obj == null) {
            return true;
        }
        
        return obj instanceof HttpServletRequest || 
               obj instanceof HttpServletResponse ||
               obj.getClass().getName().contains("springframework.web") ||
               obj.getClass().getName().contains("servlet");
    }

    /**
     * 查找指定名称的字段（支持继承）
     */
    private Field findField(Class<?> clazz, String fieldName) {
        Class<?> currentClass = clazz;
        while (currentClass != null && currentClass != Object.class) {
            try {
                return currentClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                currentClass = currentClass.getSuperclass();
            }
        }
        return null;
    }
}
